﻿using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Collections;
using System.Web;

[assembly: WebResource("UIControls.Repeater.Repeater.js", "text/javascript")]
[assembly: WebResource("UIControls.Repeater.expand.gif", "img/gif")]
[assembly: WebResource("UIControls.Repeater.collapse.gif", "img/gif")]

namespace UIControls
{
    public class Repeater : HierarchicalDataBoundControl, INamingContainer, IScriptControl
    {
        #region Constructors

        static Repeater()
        {
            EventItemCreated = new object();
            EventItemDataBound = new object();
            EventItemCommand = new object();
        }

        #endregion

        #region Private Members
        
        private static readonly object EventItemCreated;
        private static readonly object EventItemDataBound;
        private static readonly object EventItemCommand;
        private ScriptManager _scriptManager;
        private ITemplate _itemBodyTemplate;
        private ITemplate _itemHeaderTemplate;
        private ITemplate _itemFooterTemplate;
        
        #endregion

        #region Public Properties
        
        [PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
        public ITemplate HeaderTemplate
        {
            get
            {
                return this._itemHeaderTemplate;
            }
            set
            {
                this._itemHeaderTemplate = value;
            }
        }

        [PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
        public ITemplate BodyTemplate
        {
            get
            {
                return this._itemBodyTemplate;
            }
            set
            {
                this._itemBodyTemplate = value;
            }
        }

        [PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
        public ITemplate FooterTemplate
        {
            get
            {
                return this._itemFooterTemplate;
            }
            set
            {
                this._itemFooterTemplate = value;
            }
        }

        [Bindable(true)]
        public string ExpandImageUrl
        {
            get
            {
                if (ViewState["ExpandImageUrl"] != null)
                    return (string)ViewState["ExpandImageUrl"];
                else
                    return this.GetWebResourceUrl("UIControls.Repeater.expand.gif");
            }
            set
            {
                this.ViewState["ExpandImageUrl"] = value;
            }
        }

        [Bindable(true)]
        public string CollapseImageUrl
        {
            get
            {
                if (ViewState["CollapseImageUrl"] != null)
                    return (string)ViewState["CollapseImageUrl"];
                else
                    return this.GetWebResourceUrl("UIControls.Repeater.collapse.gif");
            }
            set
            {
                this.ViewState["CollapseImageUrl"] = value;
            }
        }

        #endregion

        #region Protected Methods

        protected override void PerformDataBinding()
        {
            base.PerformDataBinding();

            // Do not attempt to bind data if there is no
            // data source set.
            if (!this.IsBoundUsingDataSourceID && (this.DataSource == null))
                return;

            int itemCount = 0;
            IHierarchicalDataSource hierarchicalDataSource = this.DataSource as IHierarchicalDataSource;
            if (hierarchicalDataSource != null)
            {
                HierarchicalDataSourceView hierarchicalDataSourceView = hierarchicalDataSource.GetHierarchicalView(string.Empty);
                if (hierarchicalDataSourceView != null)
                {
                    IHierarchicalEnumerable hierarchicalEnumerable = hierarchicalDataSourceView.Select();
                    this.RecurseDataBindInternal(this, hierarchicalEnumerable, ref itemCount, true);
                }
            }
        }

        protected void RecurseDataBindInternal(Control parentControl, IHierarchicalEnumerable hierarchicalEnumerable, ref int itemIndex, bool useDataSource)
        {
            IEnumerator enumerator = hierarchicalEnumerable.GetEnumerator();
            while (enumerator.MoveNext())
            {
                IHierarchyData hierarchyData = hierarchicalEnumerable.GetHierarchyData(enumerator.Current);
                this.CreateItem(parentControl, ref itemIndex, RepeaterListItemType.ItemHeaderTemplate, hierarchyData);
                RepeaterItem RepeaterItem = this.CreateItem(parentControl, ref itemIndex, RepeaterListItemType.ItemBodyTemplate, hierarchyData);
                this.RecurseDataBindInternal(RepeaterItem, hierarchyData.GetChildren(), ref itemIndex, useDataSource);
                this.CreateItem(parentControl, ref itemIndex, RepeaterListItemType.ItemFooterTemplate, hierarchyData);
            }
        }
            
        protected RepeaterItem CreateItem(Control parentControl, ref int itemIndex, RepeaterListItemType listType, IHierarchyData data)
        {
            RepeaterItem repeaterItem = RepeaterItemFactory.GetRepeaterItem(itemIndex, listType);

            RepeaterItemEventArgs args = new RepeaterItemEventArgs(repeaterItem);
            this.InitializeItem(repeaterItem);
            repeaterItem.DataItem = data.Item;
            this.OnItemCreated(args);
            parentControl.Controls.Add(repeaterItem);
            repeaterItem.DataBind();
            this.OnItemDataBound(args);
            
            itemIndex++;
            return repeaterItem;
        }

        protected void InitializeItem(RepeaterItem item)
        {
            ITemplate template = null;

            if (item is RepeaterHeaderItem)
                template = this._itemHeaderTemplate;
            if (item is RepeaterBodyItem)
                template = this._itemBodyTemplate;
            if (item is RepeaterFooterItem)
                template = this._itemFooterTemplate;
            
            if (template != null)
                template.InstantiateIn(item);
        }

        protected string GetWebResourceUrl(string resource)
        {
            return Page.ClientScript.GetWebResourceUrl(this.GetType(), resource);
        }

        protected override bool OnBubbleEvent(object source, EventArgs args)
        {
            if (args is RepeaterCommandEventArgs)
            {
                this.OnItemCommand((RepeaterCommandEventArgs)args);
                return true;
            }
            return false;

        }
        public virtual void OnItemCommand(RepeaterCommandEventArgs e)
        {
            RepeaterCommandEventHandler handler = (RepeaterCommandEventHandler)base.Events[Repeater.EventItemCommand];
            if (handler != null)
                handler(this, e);

        }
        public virtual void OnItemCreated(RepeaterItemEventArgs args)
        {
            RepeaterItemEventHandler handler = (RepeaterItemEventHandler)base.Events[Repeater.EventItemCreated];
            if (handler != null)
                handler(this, args);
        }

        public virtual void OnItemDataBound(RepeaterItemEventArgs args)
        {
            RepeaterItemEventHandler handler = (RepeaterItemEventHandler)base.Events[EventItemDataBound];
            if (handler != null)
                handler(this, args);
        }

        #endregion

        #region EventHandlers
        [Category("Action"), Description("Repeater_OnItemCommand")]
        public event RepeaterCommandEventHandler ItemCommand
        {
            add
            {
                base.Events.AddHandler(Repeater.EventItemCommand, value);
            }
            remove
            {
                base.Events.RemoveHandler(Repeater.EventItemCommand, value);
            }

        }
        [Category("Behavior")]
        public event RepeaterItemEventHandler ItemDataBound
        {
            add
            {
                base.Events.AddHandler(Repeater.EventItemDataBound, value);
            }
            remove
            {
                base.Events.RemoveHandler(Repeater.EventItemDataBound, value);
            }
        }
        [Category("Behavior"), Description("DataControls_OnItemCreated")]
        public event RepeaterItemEventHandler ItemCreated
        {
            add
            {
                base.Events.AddHandler(Repeater.EventItemCreated, value);
            }
            remove
            {
                base.Events.RemoveHandler(Repeater.EventItemCreated, value);
            }
        }
        #endregion

        #region Base Class Overrides
        
        protected override void OnPreRender(EventArgs e)
        {
            this._scriptManager = ScriptManager.GetCurrent(Page);

            if (this._scriptManager == null)
                throw new InvalidOperationException("ScriptManager required on the page.");

            this._scriptManager.RegisterScriptControl(this);
            
            base.OnPreRender(e);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (!this.DesignMode)
            {
                if (this._scriptManager == null)
                    throw new HttpException("A ScriptManager control must exist on the current page.");
                this._scriptManager.RegisterScriptDescriptors(this);
            }
            base.Render(writer);
        }
        #endregion

        #region IScriptControl Members

        public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            ScriptControlDescriptor descriptor = new ScriptControlDescriptor("UIControls.Repeater", this.ClientID);
            descriptor.AddProperty("expandImageUrl", this.ExpandImageUrl);
            descriptor.AddProperty("collapseImageUrl", this.CollapseImageUrl);

            return new ScriptDescriptor[] { descriptor };
        }

        public IEnumerable<ScriptReference> GetScriptReferences()
        {
            ScriptReference reference = new ScriptReference();
            if (Page != null)
                reference.Path = Page.ClientScript.GetWebResourceUrl(this.GetType(), "UIControls.Repeater.Repeater.js");

            return new ScriptReference[] { reference };
        }

        #endregion
    }
}